package org.thymeleaf.spring.support;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ThymeleafLayoutInterceptor extends HandlerInterceptorAdapter {

	private static final String	DEFAULT_LAYOUT_PREFIX		= "/layouts";
	private static final String	DEFAULT_LAYOUT				= "/default";
	private static final String	DEFAULT_VIEW_ATTRIBUTE_NAME	= "view";

	private String				defaultLayout				= DEFAULT_LAYOUT_PREFIX + DEFAULT_LAYOUT;
	private String				viewAttributeName			= DEFAULT_VIEW_ATTRIBUTE_NAME;

	public void setDefaultLayout(String defaultLayout) {
		Assert.hasLength(defaultLayout);
		this.defaultLayout = StringUtils.trim(defaultLayout);
	}

	public void setViewAttributeName(String viewAttributeName) {
		Assert.hasLength(defaultLayout);
		this.viewAttributeName = StringUtils.trim(viewAttributeName);
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		if (modelAndView == null || !modelAndView.hasView()) {
			return;
		}

		String originalViewName = modelAndView.getViewName();
		if (ignored(originalViewName)) {
			return;
		}
		String layoutName = getLayoutName(handler);
		layoutName = StringUtils.trimToNull(layoutName);
		if (null == layoutName) {
			return;
		}
		modelAndView.setViewName(layoutName);
		modelAndView.addObject(this.viewAttributeName, originalViewName);
	}

	private boolean ignored(String viewName) {
		return viewName.startsWith("redirect:") || viewName.startsWith("forward:");
	}

	private String getLayoutName(Object handler) {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Layout layout = getMethodOrTypeAnnotation(handlerMethod);
		if (layout == null) {
			return this.defaultLayout;
		} else {
			return DEFAULT_LAYOUT_PREFIX.concat(layout.value());
		}
	}

	private Layout getMethodOrTypeAnnotation(HandlerMethod handlerMethod) {
		Layout layout = handlerMethod.getMethodAnnotation(Layout.class);
		if (layout == null) {
			return handlerMethod.getBeanType().getAnnotation(Layout.class);
		}
		return layout;
	}
}